//-----------------------------------------------
//              LCD.c
//      Liquid Crystal Display interface library
//      Arthur Benemann 21/03/2012
//      Based on Dangerous Prototypes  Buspirate Library
//-----------------------------------------------
//      Description:
//  Driver for a alphanumeric LCD, the display should be connected thought
//  a 74HCT595 (serial to parralel) IC.
//-----------------------------------------------
//      Requeriments:
//  SPI module
//  2 Remapable pin
//  1 I/O pin
//-----------------------------------------------

#include "LCD.h"
#include "picDev.h"


// SPI port selection
#ifdef LCD_SPI1
    #define SPIBUF  SPI1BUF
    #define SPICON  SPI1CON1
    #define SPISTAT SPI1STAT
    #define SPIRFUL SPI1STATbits.SPIRBF
#elif defined LCD_SPI2
    #define SPIBUF  SPI2BUF
    #define SPICON  SPI2CON1
    #define SPISTAT SPI2STAT
    #define SPIRFUL SPI2STATbits.SPIRBF
#else
    #if(!defined LCD_SPI3)
        #warning "No LCD-SPI port Selected.Using SPI3 as default"
    #endif
    #define SPIBUF  SPI3BUF
    #define SPICON  SPI3CON1
    #define SPISTAT SPI3STAT
    #define SPIRFUL SPI3STATbits.SPIRBF
#endif


//74HTC595-LCD connection
#define HCT595_LCD_RS 0b00000010
#define HCT595_LCD_EN 0b00001000

//LCD register select (RS)
#define LCD_COMMAND 0 
#define LCD_DATA 1

//LCD Commands
#define LCD_CLEARDISPLAY    0x01    // 1.64ms
#define LCD_RETURNHOME      0x02    // 1.64ms
#define LCD_ENTRYMODESET    0x04    //40us
    #define INCREMENT           0b10
    #define DECREMENT           0b00
    #define DISPLAYSHIFTON      0b1
    #define DISPLAYSHIFTOFF     0
#define LCD_DISPLAYCONTROL  0x08    //40us
    #define DISPLAYON           0b100
    #define DISPLAYOFF          0
    #define CURSERON            0b10
    #define CURSEROFF           0
    #define BLINKON             0b1
    #define BLINKOFF            0
#define LCD_CURSORSHIFT     0x10    //40us
    #define DISPLAYSHIFT        0b1000
    #define CURSERMOVE          0
    #define SHIFTRIGHT          0b100
    #define SHIFTLEFT           0
#define LCD_FUNCTIONSET     0x20    //40us
    #define MULTIPLELINES   0b1000
#define LCD_SETCGRAMADDR    0x40    //40us
#define LCD_SETDDRAMADDR    0x80    //40us

// MACROS
#define LCD_latch() LCD_CS = 0;LCD_CS = 1

// Functions
unsigned char writeSPI( unsigned char b);
void write4bitLCD(unsigned char rs, unsigned char dat);
void writeLCD(unsigned char rs, unsigned char dat);

// Delay 40us
void delay40us(void){
    unsigned int i;
    for(i=0;i<(3*40);i++);
}

void initLCD( void) {
    #ifdef LCD_SPI1
        LCD_SDI = Ro_SCK1OUT;
        LCD_SCK = Ro_SDO1;   // FIXME
    #elif defined LCD_SPI2
        LCD_SDI = Ro_SCK2OUT;
        LCD_SCK = Ro_SDO2;  //FIXME
    #else
        LCD_SDI = Ro_SCK3OUT;
        LCD_SCK = Ro_SDO3; //FIXME
    #endif

    LCD_CS = 1;
    LCD_CS_tris = 0;           // make Chip Seletc pin an output
  
    // init the spi module for a slow (safe) clock speed first
    SPICON = 0x013b;    // CKE=1; CKP=0, sample middle,  1:2
    SPISTAT = 0x8000;   // enable the peripheral


    //LCD initialization, Enable Four Bit Mode
    writeSPI(0);         //clear HTC595 pins
    delayms(15);
    write4bitLCD(LCD_COMMAND, 0x03);
    delayms(5);
    write4bitLCD(LCD_COMMAND, 0x03);
    delayms(5);
    write4bitLCD(LCD_COMMAND, 0x03);
    delayms(5);
    write4bitLCD(LCD_COMMAND, 0x02);
    delayms(5);

    //Function set
    cmdLCD(LCD_FUNCTIONSET + MULTIPLELINES);
    //Turn display off
    cmdLCD(LCD_DISPLAYCONTROL + DISPLAYOFF + CURSEROFF + BLINKOFF);
    //Clear LCD and return home
    clearLCD();
    //Turn on display, turn off cursor and blink
    cmdLCD( LCD_DISPLAYCONTROL + DISPLAYON + CURSERON + BLINKOFF);
}

void clearLCD(){
  cmdLCD(LCD_CLEARDISPLAY);
  delayms(2);
}

void cursorLCD(unsigned char row,unsigned char col){
  row =(row)?0x40:0x00;
  cmdLCD(LCD_SETDDRAMADDR + col + row);
}

void putsLCD( char *str){
    while(*str)
        putLCD(*str++);
}

//write CMD to LCD
void cmdLCD( unsigned char dat){
        writeLCD(LCD_COMMAND,dat);
}

//write char to LCD
void putLCD( unsigned char dat){
        writeLCD(LCD_DATA,dat);
}

//write byte to LCD
void writeLCD(unsigned char rs, unsigned char dat){
        write4bitLCD(rs, (dat>>4) );
        write4bitLCD(rs, (dat & 0x0F));
        delay40us();
}

//write 4 bits to the LCD
void write4bitLCD(unsigned char rs, unsigned char dat){
        dat=dat<<4;

        if(rs){dat |= HCT595_LCD_RS;}

        writeSPI(dat); //load values

        dat |= HCT595_LCD_EN; //raise the EN line to clock in the values
        writeSPI(dat);

        dat &= (~HCT595_LCD_EN); //lower EN line
        writeSPI(dat);
}

// send one byte of data and receive one back at the same time
unsigned char writeSPI( unsigned char b){
    SPIBUF = b;                    // write to buffer for TX
    while( !SPIRFUL);              // wait transfer complete
    LCD_latch();
    return SPIBUF;                 // read the received value
}






